﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Collections;

namespace homeworkhero
{
    public class Stage
    {
        private Random rand = new Random();
        private Texture2D background;
        private Texture2D[] problems;
        private LinkedList<CollidableSprite> sprites = new LinkedList<CollidableSprite>();
        private LinkedList<Player> players = new LinkedList<Player>();
        private ArrayList enemies = new ArrayList();
        private Boss boss;

        private bool keepSpawningEnemies = true;
        private TimeSpan timeTilNextSpawn;

        public Stage(Texture2D bg, Texture2D[] problems) {
            this.background = bg;
            timeTilNextSpawn = TimeSpan.Zero;
            this.problems = problems;
        }

        public void update(GameTime time) {
            removeDeadSprites();
            if ((boss.getPosition().X - players.First.Value.getPosition().X) < 4000f)
            {
                boss.setActive(true);
            }

            if (boss.getActive() != true)
            {
                if (keepSpawningEnemies)
                {

                    timeTilNextSpawn = timeTilNextSpawn.Subtract(time.ElapsedGameTime);
                    if (timeTilNextSpawn.CompareTo(TimeSpan.Zero) <= 0)
                    {
                        timeTilNextSpawn = TimeSpan.FromMilliseconds(rand.NextDouble() * 5000.0);

                        Enemy newEnemy = pickARandomEnemy();
                        newEnemy.setPosition(new Vector2(getPlayerX() + 400 + (float)rand.NextDouble() * (2.0f * Game1.SCREENWIDTH / 3.0f), (float)rand.NextDouble() * 5.0f));
                        sprites.AddLast(newEnemy);
                    }
                }
            }
            LinkedList<CollidableSprite>.Enumerator e = sprites.GetEnumerator();
            while (e.MoveNext())
            {
                e.Current.update(time);
            }
        }

        public LinkedList<Player> getPlayers() {
            return players;
        }

        public LinkedList<CollidableSprite> getSprites() {
            return sprites;
        }

        public float getPlayerX() {
            float sum = 0.0f;
            LinkedList<Player>.Enumerator e = players.GetEnumerator();
            while(e.MoveNext()) {
                sum += e.Current.getPosition().X;
            }

            return sum / players.Count;
        }

        public void draw(SpriteBatch b) {
            float firstBackgroundRectangle = (int)(Game1.SCREENWIDTH / 3.0f) - (getPlayerX() % background.Width) + 45;
            b.Draw(background, new Vector2(firstBackgroundRectangle, 0.0f),
                new Rectangle(0, 0, background.Width, background.Height), Color.White, 0.0f,
                Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f);
            b.Draw(background, Vector2.Zero, new Rectangle((int)(background.Width - firstBackgroundRectangle),
                0, (int)firstBackgroundRectangle, background.Height), Color.White, 0.0f, Vector2.Zero, 1.0f,
                SpriteEffects.None, 1.0f);
            b.Draw(background, new Vector2(firstBackgroundRectangle + background.Width, 0.0f),
                new Rectangle(0, 0, background.Width, background.Height), Color.White, 0.0f, Vector2.Zero,
                1.0f, SpriteEffects.None, 1.0f);

            float numOfPixelsBetweenImages = Game1.SCREENWIDTH + problems[0].Width;

            int theProblemRectangle = (int)((getPlayerX() + (2.0f*Game1.SCREENWIDTH/3.0f)) / numOfPixelsBetweenImages) % problems.Length;

            /* there is probably a O(1) equivalent of this... */
            while(theProblemRectangle < 0) {
                theProblemRectangle += problems.Length;
            }

            float nearestProblemRectangle = (float)Math.Floor((getPlayerX() + (2.0f*Game1.SCREENWIDTH/3.0f)) / (numOfPixelsBetweenImages * problems.Length)) * (numOfPixelsBetweenImages * problems.Length) + theProblemRectangle * numOfPixelsBetweenImages;
            nearestProblemRectangle = (Game1.SCREENWIDTH / 3.0f) - getPlayerX() + nearestProblemRectangle;
            b.Draw(problems[theProblemRectangle], new Vector2(nearestProblemRectangle, 30), Color.White);

            LinkedList<CollidableSprite>.Enumerator e = sprites.GetEnumerator();
            while(e.MoveNext()) {
                e.Current.draw(b);
            }
        }

        public void setSpawnEnemies(bool spawn)
        {
            this.keepSpawningEnemies = spawn;
        }

        public void addEnemyType(Enemy e) {
            enemies.Add(e);
        }

        public void addSprite(CollidableSprite s)
        {
            if (s is Player)
            {
                players.AddLast((Player)s);
            }
            sprites.AddLast(s);
        }

        public void removeSprite(CollidableSprite s)
        {
            if (s is Player)
            {
                players.Remove((Player)s);
            }
            sprites.Remove(s);
        }

        private void removeDeadSprites()
        {
            LinkedList<CollidableSprite> spritesToRemove = new LinkedList<CollidableSprite>();

            LinkedList<CollidableSprite>.Enumerator e = sprites.GetEnumerator();
            while (e.MoveNext())
            {
                if (e.Current.isDead())
                {
                    spritesToRemove.AddLast(e.Current);
                }
            }

            e = spritesToRemove.GetEnumerator();
            while (e.MoveNext())
            {
                sprites.Remove(e.Current);
            }
        }

        public void removeAllSprites()
        {
            sprites.Clear();
        }

        private Enemy pickARandomEnemy()
        {
            Enemy theEnemy = (Enemy)enemies[rand.Next(enemies.Count)];
            return theEnemy.copyEnemy();
        }

        private Enemy makeBoss()
        {
            Enemy theEnemy = (Enemy)enemies[rand.Next(enemies.Count)];
            return theEnemy.copyEnemy();
        }

        public void initializeBoss()
        {
            boss = new Boss(this, makeBoss(), new Vector2(30000f, 1.0f), 70);
            addSprite(boss);
        }
    }
}
